﻿// This file is part of Code4Public (http://code4public.codeplex.com)
// Copyright 2011 Sina Iravanian - <sina@sinairv.com>
//
// This source file(s) may be redistributed, altered and customized
// by any means PROVIDING the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
// ------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Code4Public.GUI;
using System.Diagnostics;
using Code4Public.Settings;
using Code4Public.Languages;
using Code4Public.Parsing;
using System.Xml.Linq;
using Code4Public.Utils;
using System.IO;

namespace Code4Public
{
    public partial class FormMain : Form
    {
        #region Private Fields
        private Core core = null; 
        private SyntaxHighlighter syntaxHighlighter = null;
        private HtmlGenerator htmlGenerator = null;
        #endregion

        #region Constructor
        public FormMain()
        {
            core = new Core();
            CoreInstance = core;

            InitializeComponent();

            pasteModeToolStripMenuItem.Text = CodeConstants.GUI.CopyPasteModeCaption;
            noHighlightingToolStripMenuItem.Text = CodeConstants.GUI.NoHighlightingModeCaption;
            plainHTMLToolStripMenuItem.Text = CodeConstants.GUI.PlainHTMLThemeCaption;

            syntaxHighlighter = new SyntaxHighlighter(this.rtbInput, core);
            htmlGenerator = new HtmlGenerator(core);

            AddLanguageNames(core.Languages.LanguageNames);
            SelectLanguage(AppSettings.Settings.DefaultLanguageName);

            AddColorProfiles(core.ColorProfiles.ProfileNames);
            SelectColorProfile(AppSettings.Settings.DefaultColorProfileName);

            AddThemes(core.Themes.ThemeNames);
            SelectTheme(AppSettings.Settings.DefaultThemeName);

            splitContainerHTMLCode.Panel1Collapsed = btnEmbedStyles.Checked;
            FocusShowButton(btnShowSplit);

            rtbInput.Select();
        }
        public static Core CoreInstance = null;
        #endregion

        #region Language, Highlight Mode, and Theme menus stuff

        /// <summary>
        /// Adds the color profiles by name to the Highlight Modes menu.
        /// </summary>
        /// <param name="names">array of color-profile names</param>
        private void AddColorProfiles(IEnumerable<string> names)
        {
            Debug.Assert(names != null);

            // first sort the names array
            var orderedNames = names.OrderBy(str => str);

            foreach (var itemName in orderedNames)
            {
                var item = new ToolStripMenuItem 
                {Text = itemName, Tag = itemName};

                item.Click += highlightModeItem_Click;
                highlightModesToolStripMenuItem.DropDownItems.Add(item);
            }
        }

        private void ResetThemesMenuItems()
        {
            for(int i = themesToolStripMenuItem.DropDownItems.Count - 1; i >= 0; --i)
            {
                if (themesToolStripMenuItem.DropDownItems[i] != plainHTMLToolStripMenuItem)
                    themesToolStripMenuItem.DropDownItems.RemoveAt(i);
            }
        }

        /// <summary>
        /// Adds the themes by name to the Themes menu.
        /// </summary>
        /// <param name="names">array of theme names</param>
        private void AddThemes(IEnumerable<string> names)
        {
            Debug.Assert(names != null);

            ResetThemesMenuItems();

            // first sort the names array
            var orderedNames = names.OrderBy(str => str);

            foreach (string itemName in orderedNames)
            {
                var item = new ToolStripMenuItem {Text = itemName, Tag = itemName};

                item.Click += themeItem_Click;
                themesToolStripMenuItem.DropDownItems.Add(item);
            }
        }

        /// <summary>
        /// Adds the language names to the languages menu.
        /// </summary>
        /// <param name="names">The language names.</param>
        private void AddLanguageNames(IEnumerable<string> names)
        {
            Debug.Assert(names != null);

            // first sort the names array
            var orderedNames = names.OrderBy(str => str);

            foreach (string itemName in orderedNames)
            {
                var item = new ToolStripMenuItem {Text = itemName, Tag = itemName};

                item.Click += languagesItem_Click;
                languagesToolStripMenuItem.DropDownItems.Add(item);
            }
        }

        /// <summary>
        /// Selects a color profile with the given name both visually, and logically.
        /// If the passed parameter is null, it then switches to the no highlighting mode.
        /// </summary>
        private void SelectColorProfile(string name)
        {
            ToolStripMenuItem menuItem = null;
            if (name == null)
            {
                menuItem = noHighlightingToolStripMenuItem;
                core.ColorProfiles.SelectNoProfiles();
            }
            else
            {
                try
                {
                    core.ColorProfiles.SelectedProfileName = name;
                    foreach (ToolStripMenuItem item in highlightModesToolStripMenuItem.DropDownItems)
                    {
                        if (item.Tag != null && item.Tag.ToString() == name)
                        {
                            menuItem = item;
                            break;
                        }
                    }

                }
                catch (ArgumentException)
                {
                    MessageBox.Show(String.Format("No such color profile: {0}!", name));

                    // then go back to no highlighting mode
                    menuItem = noHighlightingToolStripMenuItem;
                    core.ColorProfiles.SelectNoProfiles();
                }
            }

            if (menuItem != null)
            {
                SetMenuChildrenCheckedState(highlightModesToolStripMenuItem, false);
                menuItem.Checked = true;
                syntaxHighlighter.ReHighlight();
                GenerateHTML();
            }
        }

        /// <summary>
        /// Selects a theme with the given name both visually, and logically.
        /// If the passed parameter is null, it then switches to the no themes mode.
        /// </summary>
        private void SelectTheme(string name)
        {
            ToolStripMenuItem menuItem = null;
            if (name == null)
            {
                menuItem = plainHTMLToolStripMenuItem;
                core.Themes.SelectNoThemes();
            }
            else
            {
                try
                {
                    core.Themes.SelectedThemeName = name;

                    foreach (ToolStripMenuItem item in themesToolStripMenuItem.DropDownItems)
                    {
                        if (item.Tag != null && item.Tag.ToString() == name)
                        {
                            menuItem = item;
                            break;
                        }
                    }
                }
                catch(ArgumentException)
                {
                    MessageBox.Show(String.Format("No such theme: {0}!", name));

                    // then go back to no themes mode
                    menuItem = plainHTMLToolStripMenuItem;
                    core.Themes.SelectNoThemes();
                }
            }

            if (menuItem != null)
            {
                SetMenuChildrenCheckedState(themesToolStripMenuItem, false);
                menuItem.Checked = true;

                GenerateHTML();
            }
        }

        /// <summary>
        /// Selects a language with the given name both visually, and logically.
        /// If the passed parameter is null, it then switches to the paste-mode.
        /// </summary>
        private void SelectLanguage(string name)
        {
            ToolStripMenuItem menuItem = null;
            if (name == null)
            {
                syntaxHighlighter.DisableHighlighting = true;
                menuItem = pasteModeToolStripMenuItem;
                core.Languages.SelectNoLanguage();
            }
            else
            {
                try
                {
                    core.Languages.SelectedLanguageName = name;

                    foreach (ToolStripMenuItem item in languagesToolStripMenuItem.DropDownItems)
                    {
                        if (item.Tag != null && item.Tag.ToString() == name)
                        {
                            syntaxHighlighter.DisableHighlighting = false;
                            menuItem = item;
                            break;
                        }
                    }
                }
                catch (ArgumentException)
                {
                    MessageBox.Show(String.Format("No such language: {0}!", name));

                    // then go back to copy-paste mode
                    syntaxHighlighter.DisableHighlighting = true;
                    core.Languages.SelectNoLanguage();

                    menuItem = pasteModeToolStripMenuItem;
                }
            }

            if (menuItem != null)
            {
                SetMenuChildrenCheckedState(languagesToolStripMenuItem, false);
                menuItem.Checked = true;
                syntaxHighlighter.ReHighlight();
                GenerateHTML();
            }
        }

        /// <summary>
        /// Handles the Click event of any of the languages menu items.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void languagesItem_Click(object sender, EventArgs e)
        {
            SetMenuChildrenCheckedState(languagesToolStripMenuItem, false);
            ToolStripMenuItem item = (ToolStripMenuItem)sender;
            SelectLanguage(item.Tag as string);
        }

        /// <summary>
        /// Handles the Click event of any of the highlight mode menu items.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void highlightModeItem_Click(object sender, EventArgs e)
        {
            SetMenuChildrenCheckedState(highlightModesToolStripMenuItem, false);
            ToolStripMenuItem item = (ToolStripMenuItem)sender;
            SelectColorProfile(item.Tag as string);
        }

        /// <summary>
        /// Handles the Click event of any of the theme menu items.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void themeItem_Click(object sender, EventArgs e)
        {
            SetMenuChildrenCheckedState(themesToolStripMenuItem, false);
            ToolStripMenuItem item = (ToolStripMenuItem)sender;
            SelectTheme(item.Tag as string);
        }

        /// <summary>
        /// Sets the checked state of a menu's all children.
        /// </summary>
        /// <param name="menuItem">The menu item.</param>
        /// <param name="checkedState">the checked state to be set.</param>
        private void SetMenuChildrenCheckedState(ToolStripMenuItem menuItem, bool checkedState)
        {
            foreach (ToolStripMenuItem item in menuItem.DropDownItems)
            {
                item.Checked = checkedState;
            }
        }

        #endregion

        #region Show Buttons Events

        /// <summary>
        /// Handles the Click event of the btnShowInput control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnShowInput_Click(object sender, EventArgs e)
        {
            splitContainerMain.Panel2Collapsed = true;
            FocusShowButton(btnShowInput);
        }

        /// <summary>
        /// Handles the Click event of the btnShowPreview control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnShowPreview_Click(object sender, EventArgs e)
        {
            splitContainerMain.Panel1Collapsed = true;
            tabControlPreview.SelectedTab = tabPageWebPreview;
            FocusShowButton(btnShowPreview);
        }

        /// <summary>
        /// Handles the Click event of the btnShowSplit control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnShowSplit_Click(object sender, EventArgs e)
        {
            splitContainerMain.Panel1Collapsed = false;
            splitContainerMain.Panel2Collapsed = false;
            FocusShowButton(btnShowSplit);
        }

        /// <summary>
        /// Handles the Click event of the btnShowHTML control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnShowHTML_Click(object sender, EventArgs e)
        {
            splitContainerMain.Panel1Collapsed = true;
            tabControlPreview.SelectedTab = tabPageHTML;
            FocusShowButton(btnShowHTML);
        }

        /// <summary>
        /// Focuses the show button making it stand out.
        /// </summary>
        /// <param name="btn">The Button.</param>
        private void FocusShowButton(Button btn)
        {
            UnfocusAllShowButtons();
            btn.BackColor = CodeConstants.GUI.ShowButtionsActiveColor;
        }

        /// <summary>
        /// Unfocuses all show buttons making their back color the same.
        /// </summary>
        private void UnfocusAllShowButtons()
        {
            Color activateColor = CodeConstants.GUI.ShowButtionsDeactiveColor;
            btnShowInput.BackColor = activateColor;
            btnShowPreview.BackColor = activateColor;
            btnShowSplit.BackColor = activateColor;
            btnShowHTML.BackColor = activateColor;
        }

        #endregion

        #region HTML Generation Properties and Methods

        public string StyleCode
        {
            get
            {
                return rtbStyle.Text;
            }

            set
            {
                rtbStyle.Text = value;
            }
        }

        public string HTMLBodyCode
        {
            get
            {
                return rtbHTML.Text;
            }

            set
            {
                rtbHTML.Text = value;
            }
        }

        public bool ShowHeader
        {
            get
            {
                return btnToggleHeader.Checked;
            }
            set
            {
                btnToggleHeader.Checked = value;
            }
        }

        public bool EmbedStyles
        {
            get
            {
                return btnEmbedStyles.Checked;
            }
            set
            {
                btnEmbedStyles.Checked = value;
            }
        }

        public bool UseHighlighting
        {
            get
            {
                return btnToggleHighlighting.Checked;
            }

            set
            {
                btnToggleHighlighting.Checked = value;
            }
        }

        public bool AddLineNo
        {
            get
            {
                return btnToggleLineNumber.Checked;
            }

            set
            {
                btnToggleLineNumber.Checked = value;
            }
        }

        public string HeaderCaption { get; private set; }

        /// <summary>
        /// Invokes the main HTML Generation mechanisms.
        /// </summary>
        private void GenerateHTML()
        {
            LanguageInfo lang = core.Languages.SelectedLanguage;

            htmlGenerator.AddLineNo = this.AddLineNo;
            htmlGenerator.EmbedStyles = this.EmbedStyles;
            htmlGenerator.HeaderCaption = this.HeaderCaption;
            htmlGenerator.ShowHeader = this.ShowHeader;
            htmlGenerator.EnableHighlighting = this.UseHighlighting;

            htmlGenerator.GenerateHTML(this.rtbInput);

            this.HTMLBodyCode = htmlGenerator.HTMLBodyCode;
            this.StyleCode = htmlGenerator.StyleCode;

            RenderHTMLCode();
        }

        /// <summary>
        /// Renders the HTML code assuming that StyleCode and HTMLBodyCode propeties are set properly.
        /// </summary>
        public void RenderHTMLCode()
        {
            webBrowser.DocumentText = StyleCode + HTMLBodyCode;
        }

        #endregion

        #region Toolbar Buttons Event Handlers

        /// <summary>
        /// Handles the Click event of the toolStripButtonGenerateHTML control 
        /// which is responsible for generating HTML.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void toolStripButtonGenerateHTML_Click(object sender, EventArgs e)
        {
            GenerateHTML();
        }

        /// <summary>
        /// Handles the Click event of the btnEmbedStyles control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnEmbedStyles_Click(object sender, EventArgs e)
        {
            splitContainerHTMLCode.Panel1Collapsed = btnEmbedStyles.Checked;
            GenerateHTML();
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the tabControlPreview control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tabControlPreview_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (tabControlPreview.TabPages[tabControlPreview.SelectedIndex].Name == tabPageWebPreview.Name)
            {
                RenderHTMLCode();
            }
        }

        /// <summary>
        /// Handles the Click event of the btnToggleHeader control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnToggleHeader_Click(object sender, EventArgs e)
        {
            GenerateHTML();
        }

        /// <summary>
        /// Handles the Click event of the btnToggleLineNumber control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnToggleLineNumber_Click(object sender, EventArgs e)
        {
            GenerateHTML();
        }

        /// <summary>
        /// Handles the Click event of the btnToggleHighlighting control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnToggleHighlighting_Click(object sender, EventArgs e)
        {
            GenerateHTML();
        }

        /// <summary>
        /// Handles the Click event of the btnSetHeaderCaption control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSetHeaderCaption_Click(object sender, EventArgs e)
        {
            string result = InputDialog.ShowInputDialog("Enter Header Caption:", "Code4Public Message", this.HeaderCaption);
            if (result != null)
            {
                this.HeaderCaption = result;
                GenerateHTML();
            }
        }

        /// <summary>
        /// Handles the Click event of the btnProperties control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnProperties_Click(object sender, EventArgs e)
        {
            //Theme th = new Theme("");
            //StreamWriter sw = File.CreateText(@"c:\msdn.xml");
            //sw.WriteLine(th.ToXML().ToString(SaveOptions.None));
            //sw.Close();

            if (PropertiesForm.ShowPropertiesDialog())
                GenerateHTML();
        }

        #endregion

        private void designThemeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Theme th = ThemeSelectorForm.ShowThemeSelectorForm(core.Themes, true);
            if (th != null)
            {
                string newThemeName = InputDialog.ShowInputDialog("Please select a name for the new theme", "Theme's name", th.Name, IsNewThemeNameAccepted);
                if (newThemeName != null)
                {
                    var newTheme = (Theme)th.Clone();
                    newTheme.Name = newThemeName;
                    if (DialogResult.OK == ThemeEditorForm.ShowThemeEditorForm(newTheme))
                    {
                        core.Themes.Add(newTheme);
                        AddThemes(core.Themes.ThemeNames);
                        core.Themes.SaveToFile(AppSettings.Settings.ThemesPath);
                        SelectTheme(newTheme.Name);
                    }
                }
            }
        }

        private bool IsNewThemeNameAccepted(string themeName)
        {
            themeName = themeName.Trim();
            if (themeName.Length <= 0 || (themeName.IndexOf(' ') > 0) || (themeName.IndexOf('\t') > 0))
            {
                MessageBox.Show("The selected name is invalud!");
                return false;
            }
            else if (core.Themes.ThemeExists(themeName))
            {
                MessageBox.Show("A theme with the selected name already exists! Please choose another name.");
                return false;
            }
            else
            {
                return true;
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}
